package logica.veiculo;

import java.io.Serializable;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import logica.util.Data;
import logica.veiculo.Automovel.Porta;

/**
 * Essa classe representa os veiculos que sao alugados pela locadora.
 * @author Equipe Localisa (Usa quem precisa!)
 */
public abstract class Veiculo implements Serializable{
	
	private transient static final long serialVersionUID = 1L;
	// Atributos do veiculo
	
	private String Renavam;
	private String modelo;
	private String marca;
	private int ano;
	private int potencia;
	private Potencia tipoDePotencia;
	private Cor cor;
	private Combustivel tipoDeCombustivel;
	private Data dataDeAquisicao;
	private List<Acessorio> acessorios;
	private Estado estado;
	private String placa;
	private int nivelDoTanque;
	
	public Veiculo() {
		
			this.Renavam = null;
			this.modelo = null;
			this.marca = null;
			this.ano = 0;
			this.potencia = 0;
			this.tipoDePotencia = null;
			this.cor = null;
			this.tipoDeCombustivel = null;
			this.dataDeAquisicao = null;
			this.acessorios = null;
			this.estado = null;
			this.placa = null;
			this.nivelDoTanque = 0;
			
	}
	
	/**
	 * @return O modelo do veiculo
	 */
	
	public String getModelo() {
		return modelo;
	}

	/**
	 * Muda o modelo do veiculo
	 * @param modelo O novo modelo do veiculo
	 * @throws Exception Se o novo modelo nao for valido (nao estiver
	 * de acordo com o formato especificado no sistema)
	 */
	
	public void setModelo(String modelo) throws Exception {
		if (! validaModelo(modelo)) {
			throw new Exception("\n\t <Modelo invalido!>");
		}
		this.modelo = modelo;
	}

	/**
	 * @return A marca do veiculo
	 */
	
	public String getMarca() {
		return marca;
	}

	/**
	 * Muda a marca do veiculo
	 * @param marca A nova marca
	 * @throws Exception Se a nova marca nao for valida (nao estiver
	 * de acordo com o formato especificado no sistema)
	 */
	
	public void setMarca(String marca) throws Exception {
		if (! validaMarca(marca)) {
			throw new Exception("\n\t <Marca invalida!>");
		} 
		this.marca = marca;
	}

	/**
	 * @return A constante Cor que representa a cor do veiculo
	 */
	
	public Cor getCor() {
		return cor;
	}

	/**
	 * Muda a cor do veiculo
	 * @param cor A nova cor
	 * @throws Exception Se a nova cor for nula
	 */
	
	public void setCor(Cor cor) throws Exception{
		if ( cor == null ){
			throw new Exception("\n \t <Cor invalida!>");
		}
		this.cor = cor;
	}
	
	/**
	 * @return A constante Combustivel que representa o tipo de combustivel
	 * do veiculo
	 */
	
	public Combustivel getTipoDeCombustivel() {
		return tipoDeCombustivel;
	}

	/**
	 * Muda o tipo de combustivel do veiculo
	 * @param tipoDeCombustivel O novo tipo de combustivel
	 * @throws Exception Se o novo tipo de combustivel for nulo
	 */
	
	public void setTipoDeCombustivel(Combustivel tipoDeCombustivel) throws Exception{
		if ( tipoDeCombustivel == null ){
			throw new Exception("\n \t <Tipo de combustivel invalida!>");
		}
		this.tipoDeCombustivel = tipoDeCombustivel;
	}

	/**
	 * @return O objeto Data que representa a data de aquisicao do veiculo
	 */
	
	public Data getDataDeAquisicao() {
		return dataDeAquisicao;
	}

	/**
	 * Muda a data de aquisicao do veiculo
	 * @param dataDeAquisicao A nova data de aquisicao
	 * @throws Exception Se a nova data de aquisicao for nula
	 */
	
	public void setDataDeAquisicao(Data dataDeAquisicao) throws Exception{
		if ( dataDeAquisicao == null ){
			throw new Exception("\n \t <Data de aquisicao invalida!>");
		}
		this.dataDeAquisicao = dataDeAquisicao;
	}

	/**
	 * @return A lista de acessorios do veiculo
	 */
	
	public List<Acessorio> getAcessorios() {
		return acessorios;
	}

	/**
	 * Muda a lista de acessorios do veiculo
	 * @param acessorios A nova lista de acessorios
	 * @throws Exception Se a nova lista de acessorios for nula
	 */
	
	public void setAcessorios(List<Acessorio> acessorios) throws Exception {
		if ( acessorios == null ){
			throw new Exception("\n \t <Acessorios invalidos!>");
		}
		this.acessorios = acessorios;
	}

	/**
	 * @return A constante Estado que representa o estado do veiculo
	 */
	
	public Estado getEstado() {
		return estado;
	}

	/**
	 * Muda o estado do veiculo 
	 * @param estado O novo estado
	 * @throws Exception Se o novo estado for nulo
	 */
	
	public void setEstado(Estado estado) throws Exception {
		if ( estado == null ){
			throw new Exception("\n \t <Estado invalido!>");
		}
		this.estado = estado;
	}
	
	/**
	 * @return O numero RENAVAM do veiculo
	 */	
	
	public String getRenavam() {
		return Renavam;
	}

	/**
	 * Muda o RENAVAM do veiculo
	 * @param Renavam O novo RENAVAM
	 * @throws Exception Se a nova marca nao for valida (nao estiver
	 * de acordo com o formato especificado no sistema)
	 */
	
	public void setRenavam(String Renavam) throws Exception {
		if (! validaRenavam(Renavam)) {
			throw new Exception("\n\t<RENAVAM invalido!>");
		}
		this.Renavam = Renavam;
	}
	
	/**
	 * @return O ano de fabricacao do veiculo
	 */
	public int getAno() {
		return ano;
	}

	/**
	 * Muda o ano de fabricacao do veiculo
	 * @param ano O novo ano de fabricacao
	 * @throws Exception Se o novo ano de fabricacao for menor que 1900 ou maior que 2100
	 */
	public void setAno(int ano) throws Exception {
		if ( ano <= 1900 || ano > 2100 ){
			throw new Exception("\n \t <Ano invalido!>");
		}
		this.ano = ano;
	}

	/**
	 * @return O valor da potencia do veiculo
	 */
	
	public int getPotencia() {
		return potencia;
	}	

	/**
	 * Muda o valor da potencia do veiculo
	 * @param potencia O novo valor da potencia
	 * @throws Exception Se o novo valor da potencia for menor ou igual a 0
	 */
	
	public void setPotencia(int potencia) throws Exception {
		if (potencia <= 0) {
			throw new Exception("\n \t <Potencia invalida!>");
		}
		this.potencia = potencia;
	}
	
	/**
	 * @return A constante Potencia que representa o tipo de potencia do veiculo
	 */
	
	public Potencia getTipoDePotencia() {
		return tipoDePotencia;
	}

	/**
	 * Muda o tipo de potencia do veiculo
	 * @param tipoDePotencia O novo tipo de potencia
	 * @throws Exception Se o novo tipo de potencia eh nulo
	 */
	
	public void setTipoDePotencia(Potencia tipoDePotencia) throws Exception{
		if ( tipoDePotencia == null ){
			throw new Exception("\n \t <Tipo de Potencia invalido!>");
		}
		this.tipoDePotencia = tipoDePotencia;
	}

	/**
	 * @return A placa do veiculo
	 */
	
	public String getPlaca() {
		return placa;
	}
	
	/**
	 * Muda a placa do veiculo
	 * @param placa A nova placa
	 * @throws Exception Se a nova placa nao for valida (nao estiver
	 * de acordo com o formato especificado no sistema)
	 */
	
	public void setPlaca(String placa) throws Exception {
		if (! validaPlaca(placa)) {
			throw new Exception("\n \t <Placa invalida!>");
		}
		this.placa = placa;
	}
	
	/**
	 * @return O valor do nivel do tanque
	 */
	
	public int getNivelDoTanque() {
		return nivelDoTanque;
	}	
	
	/**
	 * Muda o valor do nivel do tanque
	 * @param nivelDoTanque O novo valor do nivel do tanque
	 * @throws Exception Se o novo valor do nivel do tanque for menor que 0 ou maior que 100
	 */
	
	public void setNivelDoTanque(int nivelDoTanque) throws Exception {
		if (nivelDoTanque < 0 || nivelDoTanque > 100) {
			throw new Exception("\n \t <Nivel do tanque invalido!>");
		}
		this.nivelDoTanque = nivelDoTanque;
	}

	// Valida o RENAVAM, verificando se o mesmo possui exatamente 9 caracteres
	// numericos, podendo haver um hifen antes do ultimo caractere
	
	
	private boolean validaRenavam(String renavam) {
		if (renavam == null || renavam.trim().isEmpty()) {
			return false;
		}
		
		Pattern pRenavam = Pattern.compile("[0-9]{8}([-])?[0-9]{1}");
		
		Matcher mRenavam = pRenavam.matcher(renavam);
		
		return mRenavam.matches();
	}
	
	// Valida a marca, verificando se a mesma eh composta apenas por caracteres
	// alfabeticos, com um total minimo de 3
	
	private boolean validaMarca(String marca) {
		if (marca == null || marca.trim().isEmpty()) {
			return false;
		}
		
		Pattern pMarca = Pattern.compile("([a-zA-Z]+([\\s][a-zA-Z]+)?){3,}");
		
		Matcher mMarca = pMarca.matcher(marca);
		
		return mMarca.matches();
	}
	
	// Valida o modelo, verificando se inicia com pelo menos 1 caractere
	// alfabetico maiusculo, podendo depois ter caracteres alfa-numericos,
	// hifens e espacos, com o tamanho total minimo de 2
	
	private boolean validaModelo(String modelo) {
		if (modelo == null || modelo.trim().isEmpty()) {
			return false;
		}
		
		Pattern pModelo = Pattern.compile("([A-Z]+(([\\s]*[-]*[\\w]*)+)?)+{2}");
		
		Matcher mModelo = pModelo.matcher(modelo);
		
		return mModelo.matches();
	}
	
	// Valida a placa, verificando se a mesma possui ao todo exatamente 7
	// caracteres, sendo os 3 primeiros caracteres alfabeticos maiusculos
	// podendo ou nao depois haver um hifen e os 4 ultimos ser caracteres
	// numericos
	
	
	private boolean validaPlaca(String placa) {
		if (placa == null || placa.trim().isEmpty()) {
			return false;
		}
		
		Pattern pPlaca = Pattern.compile("[A-Z]{3}([-])?[0-9]{4}");
		
		Matcher mPlaca = pPlaca.matcher(placa);
		
		return mPlaca.matches();
	}
	
	/**
	 * Adiciona um novo acessorio na lista de acessorios do veiculo
	 * @param novoAcessorio O acessorio a ser adicionado
	 */
	
	public void adicionaAcessorio(Acessorio novoAcessorio) {
		if (!getAcessorios().contains(novoAcessorio)) {
			getAcessorios().add(novoAcessorio);
		}
	}

	/**
	 * Remove um acessorio da lista de acessorios do veiculo
	 * @param acessorioASerRemovido O acessorio a ser removido
	 */
	
	public void removeAcessorio(Acessorio acessorioASerRemovido) {
		if (getAcessorios().contains(acessorioASerRemovido)) {
			getAcessorios().remove(acessorioASerRemovido);
		}
		
	}
	
	/**
	 * @return A constante Porta que indica o numero de portas que o veiculo contem
	 * (aplicavel apenas a Automovel)
	 */
	public abstract Porta getPortas();
	
	
	/**
	 * Muda a constante Porta que indicara o numero de portas do veiculo
	 * @param porta A nova constante Porta que indicara o numero de portas do veiculo
	 * @throws Exception Se a nova constante Porta for nula 
	 */
	public abstract void setPortas(Porta porta) throws Exception;
	
	/**
	 * Compara um objeto Locador com um outro, verificando se
	 * seus atributos login, RG e CPF sao iguais
	 * @return True se os objetos sao iguais e False caso contrario
	 */
	
	@Override
	public boolean equals(Object objeto) {
		if (!(objeto instanceof Veiculo)) {
			return false;
		}
		
		return this.getRenavam().replace("-", "").equals(((Veiculo) objeto).getRenavam().replace("-", "")) ||
		      this.getPlaca().replace("-", "").equals(((Veiculo) objeto).getPlaca().replace("-", ""));
		}
}